﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace hse_2013_272_lines
{
    public class Field
    {

        //Действующие лица
        Block[,] field = new Block[10, 20];
        public CurrentShape activeShape { get; private set; }
        CurrentShape nextShape;
        public ulong score { get; private set; }
        public uint lines { get; private set; }
        public uint speed { get; private set; }
        public bool paused;
        
        System.Windows.Forms.Timer timer1;

        public event Action GameOver;
        public event Action Redraw;

        //Логика 

        // Начинаем новую игру: генерируем 2 фигуры, чистим поле, ставим таймер
        public Field(Action GameOverHandler, Action RedrawHandler)
        {
            this.timer1 = new System.Windows.Forms.Timer();
            this.timer1.Tick += new System.EventHandler(this.timer1_Tick);

            GameOver += GameOverHandler;
            Redraw += RedrawHandler;
            
            activeShape = NewShape();
            nextShape = NewShape();

            for (int i = 0; i < 10; i++)
                for (int j = 0; j < 20; j++)
                    field[i, j] = Block.Empty;

            timer1.Interval = 750; //Полторы секунды для начала, перебор, не?
            timer1.Start();
            score = 0;
            lines = 0;
            speed = 1;
        }

        public void Pause()
        {
            timer1.Stop();
            paused = true;
        }

        public void Continue()
        {
            timer1.Start();
            paused = false;
        }

        //Рандомное задание новой фигуры из готового списка
        public CurrentShape NewShape()
        {
            Random r = new Random();
            CurrentShape sh = new CurrentShape(CurrentShape.shapes[r.Next(CurrentShape.shapes.Count())]);
            return sh;
        }

        //Проверка возможности сдвига фигуры на 1 вниз
        public bool CheckUpMove()
        { //Смотрим по горизонтали в рамках фигуры
            for (int j = activeShape.y; activeShape.y - j < activeShape.shape.GetLength(1); j--)
            {
                //Смотрим наличие всободного пространства в 
                for (int i = activeShape.x; i - activeShape.x < activeShape.shape.GetLength(0); i++)
                {
                    //Совпадение закрашенных блоков поля и закрашенных блоков фигуры
                    if (j < 1) continue;
                    if (j == 19) return false;
                    if ((field[i, j + 1] != Block.Empty) && (activeShape.shape[i - activeShape.x, activeShape.shape.GetLength(1) - activeShape.y + j - 1] != Block.Empty))
                    {
                        return false;
                    }
                }
            }
            return true;
        }
        public void FieldAddAndClear()
        {
            //Добавление блоков фигуры к полю
            for (int i = 0; i < activeShape.shape.GetLength(0); i++)
                for (int j = 0; j < activeShape.shape.GetLength(1); j++)
                    if (activeShape.shape[i, j] != Block.Empty)
                        field[i + activeShape.x, j - activeShape.shape.GetLength(1) + 1 + activeShape.y] = activeShape.shape[i, j];
            
            //Проверка на заполненныe линии
            int temp = activeShape.shape.GetLength(1);
            for (int j = activeShape.y; activeShape.y - j < temp; j--)
            {
                int sum = 0;
                for (int i = 0; i < 10; i++)
                {
                    if (field[i, j] != Block.Empty) sum++;
                }
                //Если линия заполнена
                if (sum == 10)
                {
                    //Сдвиг всех блоков на 1 вниз, начиная с нижнего(который заполнен) и двигаясь вверх
                    for (int k = j; k > 0; k--) //Или к>=0?
                    {
                        for (int i = 0; i < 10; i++)
                        {
                            field[i, k] = field[i, k - 1];
                        }

                    }
                    //Верхняя пустая линия
                    for (int i = 0; i < 10; i++)
                    {
                        field[i, 0] = Block.Empty;
                    }
                    // Изменение счетчиков
                    lines++;
                    score = score + speed * 100;
                    //Устранение лишнего прохода по строке после сдвига
                    temp--;
                    // Повторный проход: а вдруг мы сдвинули заполненную строку?
                    j++;

                }
            }

        }


        //Падают, падют листья...
        private void timer1_Tick(object sender, EventArgs e)
        {
            //Если есть возможность сдвига вниз
            if (CheckUpMove())
            {
                activeShape.y++; //перемещение вниз на 1 клетку

            }
            else //Если фигура должна остановиться
            {
                if (activeShape.y - activeShape.shape.GetLength(1) <= 0)//Проверка не выхода верхней грани фигуры за границы поля
                {
                    GameOver.Invoke();
                    return;
                }

                FieldAddAndClear();

                //проверка увеличения скорости
                if (lines != 0 && lines == (speed * 10))
                {
                    speed++;
                    timer1.Interval = (timer1.Interval * 2) / 3;
                }

                activeShape = nextShape;
                nextShape = NewShape();
            }
            if (Redraw != null)
                Redraw.Invoke();
        }

        public void TryToMoveDown()
        {
            if (CheckUpMove())
            {
                activeShape.y++; //перемещение вниз на 1 клетку
                if (Redraw != null)
                    Redraw.Invoke();
            }
        }

        public void TryToMoveLeft()
        {
            if (activeShape.x > 0)
            {
                for (int i = 0; i < activeShape.shape.GetLength(0); i++)
                    for (int j = 0; j < activeShape.shape.GetLength(1); j++)
                    {
                        if (activeShape.y - activeShape.shape.GetLength(1) + 1 + j < 0) continue;

                        if (field[activeShape.x + i - 1, activeShape.y - activeShape.shape.GetLength(1) + 1 + j] != Block.Empty &&
                            activeShape.shape[i, j] != Block.Empty)
                            return;
                    }
                activeShape.x--;
                if (Redraw != null)
                    Redraw.Invoke();
            }
        }

        public void TryToMoveRight()
        {
            if (activeShape.x + activeShape.shape.GetLength(0) < 10) //левый край фигуры в рамках поля
            {
                for (int i = 0; i < activeShape.shape.GetLength(0); i++)
                    for (int j = 0; j < activeShape.shape.GetLength(1); j++)
                    {
                        if (activeShape.y - activeShape.shape.GetLength(1) + 1 + j < 0) continue;

                        if (field[activeShape.x + i + 1, activeShape.y - activeShape.shape.GetLength(1) + 1 + j] != Block.Empty &&
                            activeShape.shape[i, j] != Block.Empty)
                            return;
                    }
                activeShape.x++;
                if (Redraw != null)
                    Redraw.Invoke();
            }
        }

        public void TryToRotate()
        {
            var rotatedShape = activeShape.Rotate();
            
            for (int i = 0; i < rotatedShape.shape.GetLength(0); i++)
                for (int j = 0; j < rotatedShape.shape.GetLength(1); j++)
                {
                    if (rotatedShape.x + rotatedShape.shape.GetLength(0) >= 10)
                        return;
                    if (rotatedShape.y - rotatedShape.shape.GetLength(1) + 1 + j < 0) continue;

                    if (field[rotatedShape.x + i, rotatedShape.y - rotatedShape.shape.GetLength(1) + 1 + j] != Block.Empty &&
                        rotatedShape.shape[i, j] != Block.Empty)
                        return;
                }

            activeShape = rotatedShape;
            if (Redraw != null)
                Redraw.Invoke();
        }

        public void Draw(Graphics g)
        {
            

            const int size = 40;
            const int offsetX = 60;
            const int offsetY = 30;

            g.DrawImage(Properties.Resources.IMG_0496, new Rectangle(0, 0, 768, 1024));

            //Рисуем сетку поля для ориентации
            for (int i = 0; i < 11; i++)
                g.DrawLine(Pens.Gray, new Point(offsetX - 5 + size * i, offsetY - 5), new Point(offsetX - 5 + size * i, offsetY + 795));
            for (int j = 0; j < 21; j++)
                g.DrawLine(Pens.Gray, new Point(55, offsetY - 5 + size * j), new Point(455, offsetY - 5 + size * j));

            for (int i = 0; i < 10; i++)
                for (int j = 0; j < 20; j++)
                    field[i, j].Draw(g, offsetX + size * i, offsetY + size * j);
            activeShape.Draw(g);
            nextShape.DrawNext(g);
        }
    }
}
